<?php
// +----------------------------------------------------------------------
// | ZGUAN [ 致力于帮助更多的中小企业物联网转型 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2024 https://www.zguan.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ZGUAN 并不是自由软件，未经许可不能去掉ZGUAN 相关版权
// +----------------------------------------------------------------------
// | Author: ZGUAN Team <admin@zguan.cn>  0769-82282996  15899929162
// +----------------------------------------------------------------------
namespace app\common\repositories\contract;

use app\helper\Time;
use think\facade\Db;
use think\exception\ValidateException;
use app\common\repositories\BaseRepository;
use app\common\dao\contract\ContractBasicDao;
use app\common\repositories\users\UsersRepository;
use app\common\repositories\bill\BillCateRepository;
use app\common\repositories\room\RoomBasicRepository;
use app\common\repositories\bill\BillBasicRepository;
use app\common\repositories\contract\ContractBillRepository;
use app\common\repositories\contract\ContractPictureRepository;
use app\common\repositories\property\basic\PropertyBasicRepository;
use app\common\repositories\property\users\PropertyUsersLogRepository;

/**
 * Class ContractBasicRepository
 *
 * @mixin ContractBasicDao
 */
class ContractBasicRepository extends BaseRepository
{

    public function __construct(ContractBasicDao $dao)
    {
        $this->dao = $dao;
    }

    public function getPropertyList(array $where, $page, $limit, int $propertyId = null)
    {
        $query = $this->dao->search($where, $propertyId);
        $count = $query->count();
        $list = $query->page($page, $limit)
            ->with([
                'userInfo' => function ($query) {
                    $query->field('id,username,mobile');
                    $query->bind(['username', 'mobile']);
                },
                'roomInfo' => function ($query) {
                    $query->bind(['full_room_name']);
                },
            ])
            ->append(['lave_days'])
            ->field('id,number,sign_type,start_date,end_date,status,room_id,property_id,user_id,add_time')
            ->order('id', 'desc')
            ->select();
        return compact('count', 'list');
    }

    public function getApiList(array $where, $page, $limit, int $propertyId = null)
    {
        $query = $this->dao->search($where, $propertyId);
        $count = $query->count();
        $list = $query->page($page, $limit)
            ->with([
                'userInfo' => function ($query) {
                    $query->field('id,username,mobile');
                    $query->bind(['username', 'mobile']);
                },
                'roomInfo' => function ($query) {
                    $query->bind(['full_room_name']);
                }
            ])
            ->field('id,number,sign_type,start_date,end_date,status,room_id,property_id,user_id,add_time')
            ->order('id', 'desc')
            ->select();
        return compact('count', 'list');
    }


    public function addInfo(array $data, int $propertyId, int $userId)
    {
        return Db::transaction(function () use ($data, $propertyId, $userId) {
            /** @var UsersRepository $userRepository */
            $userRepository = app()->make(UsersRepository::class);
            $userInfo = $userRepository->getInfoByMobile($data['lessee_info']['mobile']);
            if (!$userInfo) {
                $userdData['mobile'] = $data['lessee_info']['mobile'];
                $userdData['account'] = $data['lessee_info']['mobile'];
                $userdData['username'] = $data['lessee_info']['username'];
                $userInfo = $userRepository->register($userdData);
            }

            if ($data['is_rose'] == 1) {
                ## 算出自动涨租时间 及金额
                $data['next_rose_date'] = date($data['start_date'], strtotime("+{$data['next_years']} year"));
                $data['next_rose_rent'] = bcadd($data['room_rent'], bcmul($data['room_rent'], bcdiv($data['next_rose_rate'], 100, 2), 2), 2);
            }

            ## 合同用户
            $lesseeInfo = $data['lessee_info'];
            unset($data['lessee_info']);

            $picture = [];
            if (isset($data['picture'])) {
                $picture = $data['picture'];
            }
            unset($data['picture']);

            ## 其它费用
            $contractBillList = [];
            if (isset($data['other_fee'])) {
                $contractBillList = $data['other_fee'];
                unset($data['other_fee']);
            }

            ## 租金金额
            $roomRent = $data['room_rent'];
            unset($data['room_rent']);

            ## 租金支付方式
            $payType = $data['pay_type'];
            unset($data['pay_type']);

            ## 押金金额
            $roomDeposit = $data['room_deposit'];
            unset($data['room_deposit']);

            $data['property_id'] = $propertyId;
            $data['user_id'] = $userInfo['id'];
            $contractInfo = $this->dao->create($data);

            /** @var ContractBillRepository $contractBillRepository */
            $contractBillRepository = app()->make(ContractBillRepository::class);

            /** @var BillCateRepository $billCateRepository */
            $billCateRepository = app()->make(BillCateRepository::class);

            # 租金
            if ($roomRent > 0 && $payType > 0) {
                $contractBillData = [
                    'contract_id' => $contractInfo['id'],
                    'property_id' => $contractInfo['property_id'],
                    'cate_id' => $billCateRepository->getBillCateId($propertyId, ['is_class' => 1, 'set_finance' => 4]),
                    'pay_type' => $payType,
                    'amount' => $roomRent,
                    'status' => 1,
                    'if_del' => 1,
                    'if_bill' => 2,
                ];
                $contractBillRepository->addInfo($propertyId, $contractBillData, $userId);
            }
            # 押金
            if ($roomDeposit > 0) {
                $contractBillData = [
                    'contract_id' => $contractInfo['id'],
                    'property_id' => $contractInfo['property_id'],
                    'cate_id' => $billCateRepository->getBillCateId($propertyId, ['is_class' => 1, 'set_finance' => 3]),
                    'pay_type' => 4,
                    'amount' => $roomDeposit,
                    'status' => 1,
                    'if_del' => 1,
                    'if_bill' => 1,
                ];
                $contractBillRepository->addInfo($propertyId, $contractBillData, $userId);
            }

            if (isset($contractBillList) && is_array($contractBillList)) {
                foreach ($contractBillList as $k => $v) {
                    ## 支付方式 如不是 随租金支付 就修改
                    if ($v['pay_type'] == 4) {
                        $payType = $v['pay_type'];
                    }
                    $contractBillData = [
                        'contract_id' => $contractInfo['id'],
                        'property_id' => $contractInfo['property_id'],
                        'cate_id' => $v['cate_id'],
                        'pay_type' => $payType,
                        'amount' => $v['amount'],
                        'remark' => $v['remark'] ?? '',
                        'status' => 1,
                        'if_bill' => 3,
                    ];
                    $contractBillRepository->addInfo($propertyId, $contractBillData);
                }
            }

            /** @var RoomBasicRepository $roomRepository */
            $roomRepository = app()->make(RoomBasicRepository::class);
            $roomData['status'] = 2;
            $roomData['contract_id'] = $contractInfo['id'];
            $roomRepository->update($data['room_id'], $roomData);
            if (isset($picture) && is_array($picture)) {
                /** @var ContractPictureRepository $pictureRepository */
                $pictureRepository = app()->make(ContractPictureRepository::class);
                $pictureRepository->addPicture($contractInfo['id'], $picture);
            }

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($propertyId, $userId, 'CONTRACT.BASIC.ADD', $data);

            if (isset($lesseeInfo) && is_array($lesseeInfo)) {
                /** @var ContractLesseeRepository $lesseeRepository */
                $lesseeRepository = app()->make(ContractLesseeRepository::class);
                $lesseeInfo['is_owner'] = 1;
                $lesseeInfo['user_id'] = $userInfo['id'];
                $lesseeInfo['contract_id'] = $contractInfo['id'];
                $lesseeInfo['property_id'] = $data['property_id'];
                $lesseeRepository->addLesseeInfo($lesseeInfo, $propertyId);
            }
            event('contract.create', [
                'contractInfo' => $contractInfo,
                'user_id' => $userId,
            ]);

            if ($contractInfo['sign_audit'] == 1) {
                ## 开启自动审核
                $this->auditSuccess($contractInfo['id'], 1, $userId, '自动审核');
            }

            return $contractInfo;
        });
    }


    public function editInfo($info, array $data, int $userId = 0)
    {
        return Db::transaction(function () use ($data, $info, $userId) {
            $res2 = false;

            $picture = $data['picture'];
            unset($data['picture']);
            if ($data['is_rose'] == 1) {
                ## 算出自动涨租时间
                $data['next_rose_date'] = date($info['start_date'], strtotime("+{$data['next_years']} year"));
            }

            $res = $this->dao->update($info['id'], $data);
            if (isset($picture) && is_array($picture)) {
                /** @var ContractPictureRepository $pictureRepository */
                $pictureRepository = app()->make(ContractPictureRepository::class);
                $pictureRepository->removePicture($info['id']);
                $res2 = $pictureRepository->addPicture($info['id'], $picture);
            }

            /** @var PropertyUsersLogRepository $repository */
            $repository = app()->make(PropertyUsersLogRepository::class);
            $repository->addInfo($info['property_id'], $userId, 'CONTRACT.BASIC.EDIT', $data);

            event('contract.edit', [
                'contractInfo' => $this->dao->get($info['id']),
                'user_id' => $userId,
            ]);

            if ($data['sign_audit'] == 1 && $info['status'] == 1) {
                ## 开启自动审核
                $this->auditSuccess($info['id'], 1, $userId, '自动审核');
            }

            return ($res || $res2);
        });
    }


    public function getDetail(int $id)
    {
        $data = $this->dao->search([])
            ->with([
                'userInfo' => function ($query) {
                    $query->field('id,username,mobile');
                    $query->bind(['username', 'mobile']);
                },
                'roomInfo' => function ($query) {
                    $query->bind(['full_room_name']);
                },
            ])
            ->where('id', $id)
            ->find();
        return $data;
    }


    public function getApiDetail(int $id)
    {
        $data = $this->dao->getSearch([])
            ->where('id', $id)
            ->with([
                'userInfo' => function ($query) {
                    $query->field('id,username,mobile');
                    $query->bind(['username', 'mobile']);
                },
                'roomInfo' => function ($query) {
                    $query->bind(['full_room_name']);
                },
            ])
            ->field('id,property_id,room_id,user_id,status,bill_month,bill_add_day,bill_pay_day,sign_type,is_rose,next_years,next_rose_rate,next_rose_rent,start_date,end_date,bill_month,bill_add_day,bill_pay_day,overdue_fine,pdf_url,out_fee_flag,add_time,number')
            ->append(['picture', 'contract_rent', 'contract_deposit'])
            ->find();
        return $data;
    }


    public function batchDelete(array $ids, int $userId = 0)
    {
        $list = $this->dao->selectWhere([
            ['id', 'in', $ids]
        ]);
        if ($list) {
            foreach ($list as $k => $v) {
                if (!in_array($v['status'], [3, 5])) {
                    throw new ValidateException('限完成与作废的租赁进行此操作');
                }
                $this->deleteInfo($v, $userId);
            }
            return $list;
        }
        return [];
    }

    public function deleteInfo($info, int $userId = 0)
    {
        /** @var ContractPictureRepository $pictureRepository */
        $pictureRepository = app()->make(ContractPictureRepository::class);
        $pictureRepository->removePicture($info['id']);
        /** @var PropertyUsersLogRepository $repository */
        $repository = app()->make(PropertyUsersLogRepository::class);
        $repository->addInfo($info['property_id'], $userId, 'CONTRACT.BASIC.DEL', $info);
        $this->dao->delete($info['id']);
    }


    public function generateContractNumber(int $propertyId)
    {
        /** @var PropertyBasicRepository $propertyRepository */
        $propertyRepository = app()->make(PropertyBasicRepository::class);
        $codeInfo = $propertyRepository->getSearch(['id' => $propertyId])->value('code');
        return 'HT' . date('ymd') . rand(1111, 9999) . $codeInfo;
    }


    /**
     * 租赁审核
     * @param $contractInfo
     * @param $adminInfo
     * @param $trackType
     * @return mixed
     *
     * @author Amos
     * 2024-03-20 14:23
     */
    public function auditSuccess($contractId, int $status, int $userId = 0, $remark = null)
    {
        return Db::transaction(function () use ($contractId, $status, $userId, $remark) {
            $this->dao->update($contractId, [
                'status' => $status,
                'remark' => $remark ?? '',
            ]);
            ## 确认审核事件
            event('contract.auditFinish', [
                'userId' => $userId,
                'status' => $status,
                'remark' => $remark,
                'contractId' => $contractId,
            ]);
            return true;
        });
    }


    /**
     * 租赁续期
     * @param int $userId
     * @param int $id
     * @param string $endDate
     * @return mixed
     */
    public function extendContract($contractId, string $endDate, int $userId = 0)
    {
        return Db::transaction(function () use ($contractId, $userId, $endDate) {
            $res = $this->dao->update($contractId, [
                'end_date' => $endDate
            ]);
            ## 租赁续期事件
            event('contract.extend', [
                'userId' => $userId,
                'contractId' => $contractId,
                'endDate' => $endDate
            ]);
            return $res;
        });
    }


    /**
     * 租赁退租
     * @param int $userId
     * @param int $contractId
     * @param array $data
     * @return mixed
     */
    public function refundContract($contractId, array $data, int $userId)
    {
        $contractInfo = $this->dao->get($contractId);
        if ($data['refund_type'] == 1) {
            $contractData['status'] = 6;
        } else {
            $contractData['status'] = 3;
        }
        $contractData['end_date'] = date('Y-m-d');
        $contractData['end_remark'] = $data['remark'];
        $refundType = $data['refund_type'];

        return Db::transaction(function () use ($contractInfo, $contractData, $userId, $refundType) {

            $this->dao->update($contractInfo['id'], $contractData);
            ##  租赁退租事件
            event('contract.refund', [
                'userId' => $userId,
                'is_settlement' => $refundType,
                'contractId' => $contractInfo['id'],
                'end_remark' => $contractData['end_remark']
            ]);
            return true;
        });
    }

    /**
     *  合同作废
     * @param $contractId
     * @param array $data
     * @param int $userId
     * @return true
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-06-13 22:17
     */
    public function invalidContract($contractId, array $data, int $userId)
    {
        $this->dao->update($contractId, [
            'status' => 5,
            'end_remark' => $data['remark'],
        ]);

        ## 租赁作废事件
        event('contract.invalid', [
            'userId' => $userId,
            'contractId' => $contractId,
            'invalidRemark' => $data['remark'],

        ]);
        return true;
    }


    /**
     *  关联房间
     * @param $data
     * @param array $ids
     * @param int $userId
     * @return mixed
     *
     * @author Amos
     * 2024-03-20 12:09
     */
    public function batchRoomBound($data, array $ids, int $userId = 0)
    {
        return Db::transaction(function () use ($data, $ids, $userId) {
            /** @var RoomBasicRepository $roomRepository */
            $roomRepository = app()->make(RoomBasicRepository::class);
            $list = $roomRepository->getSearch([])
                ->whereIn('id', $ids)
                ->where('status', 1)
                ->field('id')
                ->select();
            if ($list) {
                foreach ($list as $k => $v) {
                    $roomRepository->roomRentStatus($v['id'], $data['id'], $userId);
                }
                return $list;
            }
        });
    }

    /**
     * 根据物业 ID  数据统计
     * @param int $propertyId
     * @return array
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-06-13 18:34
     */
    public function getStatistics(int $propertyId = 0)
    {
        $data = $this->dao->getSearch([])
            ->when($propertyId, function ($query) use ($propertyId) {
                $query->where('property_id', $propertyId);
            })
            ->field('id,property_id,status')
            ->group('status')
            ->column('count(*)', 'status');
        return [
            'stay_audit_num' => $data[1] ?? 0,
            'normal_num' => $data[2] ?? 0,
            'end_num' => $data[3] ?? 0,
            'stay_sign_num' => $data[4] ?? 0,
            'in_void_num' => $data[5] ?? 0,
            'expiring_seven_num' => $this->getExpiringSoonNum($propertyId, 7),
            'expiring_fifteen_num' => $this->getExpiringSoonNum($propertyId, 15),
            'expiring_thirty_num' => $this->getExpiringSoonNum($propertyId, 30),
        ];
    }


    /**
     *  根据物业 ID 获取将到期租赁数
     * @param int $propertyId
     * @return int
     * @throws \think\db\exception\DbException
     *
     * @author Amos
     * 2024-06-13 18:27
     */
    public function getExpiringSoonNum(int $propertyId = 0, $day = 30)
    {
        $today = date('Y-m-d');
        $daysAfter = date('Y-m-d', Time::daysAfter($day));
        return $this->dao->getSearch([])
            ->when($propertyId, function ($query) use ($propertyId) {
                $query->where('property_id', $propertyId);
            })
            ->where('status', 2)
            ->where('end_date', 'between', [$today, $daysAfter])
            ->count();
    }

}